///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_ALGORITHM_EDGES_H_
#define _BUOLA_IMAGE_ALGORITHM_EDGES_H_

#include <buola/image/algorithm/detail/opencv.h>

namespace buola { namespace img {

//! \name gradients, edges and corners
//@{

template<typename tFormat1,typename tFormat2>
inline void sobel(const CView<tFormat1> &pSrc,const CMView<tFormat2> &pDst,int pXOrder,int pYOrder,int pApertureSize=3)
{
    cv::Sobel(mat_wrap(pSrc),mat_wrap(pDst),MFormatToCVType<tFormat2>::mMatType,pXOrder,pYOrder,pApertureSize,1,0,cv::BORDER_REPLICATE);
}

template<typename tFormat>
inline void laplace(const CView<tFormat> &pSrc,const CMView<tFormat> &pDst,int pApertureSize=3)
{
    cvLaplace(mat_wrap(pSrc),mat_wrap(pDst),pApertureSize);
}

template<typename tF1,typename tF2>
inline void canny(const CView<tF1> &pSrc,const CMView<tF2> &pDst,
                  double pThreshold1,double pThreshold2,int pApertureSize=3)
{
    cvCanny(mat_wrap(pSrc),mat_wrap(pDst),pThreshold1,
              pThreshold2,pApertureSize);
}

template<typename tFormat,typename tPointType>
inline void find_contours(const CView<tFormat> &pSrc,std::vector<std::vector<tPointType>> &pContours,int pMode,
                          int pMethod,const tPointType &pOffset=tPointType(0,0))
{
    CvMemStorage *lStorage=cvCreateMemStorage();
    CvSeq *lContours=nullptr;

    cvFindContours(mat_wrap(pSrc),lStorage,&lContours,sizeof(CvContour),pMode,pMethod,cvPoint(pOffset.x,pOffset.y));

    if(!lContours)
    {
        pContours.clear();
        return;
    }

    CvSeq *lAllContours=cvTreeToNodeSeq(lContours,sizeof(CvSeq),lStorage);

    pContours.resize(lAllContours->total);

    int lCount=0;

    for(CvSeqBlock *i=lAllContours->first;i;i=i->next)
    {
        CvSeq **lContours=(CvSeq**)i->data;

        for(int j=0;j<i->count;j++)
        {
            pContours[lCount].clear();

            for(CvSeqBlock *k=lContours[j]->first;k;k=k->next)
            {
                CvPoint *lPoint=(CvPoint*)k->data;

                for(int l=0;l<k->count;l++)
                {
                    pContours[lCount].push_back(tPointType(lPoint[l].x,lPoint[l].y));
                }
                if(k->next==lContours[j]->first)
                    break;
            }

            lCount++;
        }
        if(i->next==lAllContours->first)
            break;
    }
}

template<typename tFormat>
inline void find_corner_subpix(const CView<tFormat> &pSrc,std::vector<CPoint_d> &pCorners,
            int pSize,int pSize2,int pMaxIters)
{
    if(pCorners.empty()) return;

    CvPoint2D32f lCorners[pCorners.size()];

    for(int i=0;i<pCorners.size();i++)
    {
        lCorners[i].x=pCorners[i].x;
        lCorners[i].y=pCorners[i].y;
    }

    cvFindCornerSubPix(mat_wrap(pSrc),lCorners,
          pCorners.size(),cvSize(pSize,pSize),cvSize(pSize2,pSize2),
          cvTermCriteria(CV_TERMCRIT_ITER,pMaxIters,0));

    for(int i=0;i<pCorners.size();i++)
        pCorners[i]={lCorners[i].x,lCorners[i].y};
}

template<typename tFormat>
inline void paint_mask_contour(const CMView<tFormat> &pI,const CView_gray8 &pM,
                  const CColor &pColor)
{
    typedef CPixel<tFormat> tPixel;
    tPixel lColor(pColor);
    tPixel lBlack(nBlack);

    for(int y=1;y<pI.Height()-1;++y)
    {
        auto lItI=pI.Row(y).begin();
        auto lItIn=pI.Row(y+1).begin();
        auto lItM=pM.Row(y).begin();
        auto lItMp=pM.Row(y-1).begin();
        auto lItMn=pM.Row(y+1).begin();

        for(int x=1;x<pI.Width()-1;++x)
        {
            if(lItM[x]!=lItM[x-1]||lItM[x]!=lItM[x+1]||
              lItM[x]!=lItMp[x]||lItM[x]!=lItMn[x])
            {
                lItI[x]=lColor;
                lItI[x+1]=lBlack;
                lItIn[x+1]=lBlack;
            }
        }
    }
}

void harris_corners(const CView_gray32f &pView,std::vector<std::pair<CPoint_d,float>> &pPeaks,
                    bool pLowPass=true,float pThresh=20.0,float pHarrFac=0.06,int pAreaSize=7);

void harris_corners(const CView_gray8 &pView,std::vector<std::pair<CPoint_d,float>> &pPeaks,
                    bool pLowPass=true,float pThresh=20.0,float pHarrFac=0.06,int pAreaSize=7);

//@}

/*namespace image*/ } /*namespace buola*/ }

#endif
